import View from './components/view'
import Link from './components/link'

export let _Vue
/**
 * 1、全局赋值_Vue并导出
 * 2、对vue进行组件钩子函数注入，并响应式_route
 * 3、在vue原型设置$router和$route
 * 4、组件内的守卫合并策略取created的合并策略
 * @param {*} Vue
 * @returns
 */

export function install (Vue) {
  if (install.installed && _Vue === Vue) return
  install.installed = true

  _Vue = Vue

  const isDef = v => v !== undefined

  const registerInstance = (vm, callVal) => {
    let i = vm.$options._parentVnode
    if (
      isDef(i) &&
      isDef((i = i.data)) &&
      isDef((i = i.registerRouteInstance))
    ) {
      i(vm, callVal)
    }
  }
  /**
   * 给每个组件混入钩子函数，在可以发现在 `beforeCreate` 钩子执行时会初始化路由
   */
  Vue.mixin({
    beforeCreate () {
      if (isDef(this.$options.router)) {
        // 根组件，只有根组件有router，this指向的是vue，
        // _routerRoot 路由根设置为自己，初始化_router
        this._routerRoot = this
        this._router = this.$options.router
        // 初始化路由
        this._router.init(this)
        // 为 _route 属性实现双向绑定,触发组件渲染,一旦_route数据发生变化后，通知router-view执行render方法
        Vue.util.defineReactive(this, '_route', this._router.history.current)
      } else {
        // 不是根组件,用于 router-view 层级判断
        this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
      }
      // 注册实例
      registerInstance(this, this)
    },
    destroyed () {
      // 销毁实例
      registerInstance(this)
    }
  })

  // 挂载变量到原型上，我们在组件中使用的$router，和$route就是在这边设置的
  // $router就是new vueRouter的返回值，也就是在new Vue时传递的router  $router == _router == new VueRouter()返回值 == new Vue()传递的router
  // $route 是当前路由，是$router的histroy的current，也是_route      $route = _route = $router.history.current == _router.history.current

  // 通过写get形式而不是直接Vue.prototype.'$router' = this._routerRoot._router 是为了让其可读，不能修改
  Object.defineProperty(Vue.prototype, '$router', {
    get () {
      return this._routerRoot._router
    }
  })

  Object.defineProperty(Vue.prototype, '$route', {
    get () {
      return this._routerRoot._route
    }
  })

  // 注册全局组件
  Vue.component('RouterView', View)
  Vue.component('RouterLink', Link)

  // 组件内的守卫合并策略取created的合并策略

  /**
   * 全局守卫：beforeEach  beforeEach
   *路由独享的守卫 ： beforeEnter（配置route时的参数）
   *组件内的守卫：beforeRouteEnter  beforeRouteUpdate  beforeRouteLeave
   */

  const strats = Vue.config.optionMergeStrategies
  // use the same hook merging strategy for route hooks
  strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate =
    strats.created
}
